package com.jahentao.patentQuery.web.controller;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.jahentao.patentQuery.config.FileUploadConfig;
import com.jahentao.patentQuery.config.ParticipleConfig;
import com.jahentao.patentQuery.constant.EhCacheKeys;
import com.jahentao.patentQuery.constant.SysUri;
import com.jahentao.patentQuery.exception.TaskException;
import com.jahentao.patentQuery.model.*;
import com.jahentao.patentQuery.model.bean.MessageCode;
import com.jahentao.patentQuery.model.bean.ResultBean;
import com.jahentao.patentQuery.model.page.QueryResult;
import com.jahentao.patentQuery.model.progress.AbstractTask;
import com.jahentao.patentQuery.model.progress.Progress;
import com.jahentao.patentQuery.service.*;
import com.jahentao.patentQuery.util.ReadExcel;
import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>专利需求业务相关 控制器</p>
 * <p>业务上，找案子，对于工作人员来说即找卖家。【买家求购案子，卖家提供案子】后半句</p>
 * @author jahentao
 * @date 2018/4/13
 * @since 1.0
 */
@Controller
@RequestMapping(SysUri.SUPPLY)
public class PatentSupplyController extends BaseController{

    @Autowired
    private PatentService patentService;
    @Autowired
    private PatentDemandService patentDemandService;
    @Autowired
    private PatentSupplyService patentSupplyService;
    @Autowired
    private ParticipleService participleService;
    @Autowired
    private UserService userService;
    @Autowired
    private PatentSupplyQueryService patentSupplyQueryService;

    @Autowired
    private ParticipleConfig participleConfig;
    @Autowired
    private FileUploadConfig fileUploadConfig;

    @Resource
    private CacheManager springCacheManager;

    /**
     * 任务编号。
     * <p>有个缺点，重启应用之后，该编号又要从1开始</p>
     * <p>也罢，就限定任务在重启之后，全部失效</p>
     */
    static final AtomicLong TASK_ID_NUMBER = new AtomicLong(1);


    // =============================页面跳转===================================

    @RequestMapping(value = "list")
    public String list() {
        return "admin/patentSupply/list";
    }

    @RequestMapping(value = "edit")
    public String edit() {
        return "admin/patentSupply/edit";
    }

    @RequestMapping(value = "add")
    public String add() {
        return "admin/patentSupply/add";
    }

    // =============================流程控制===================================

    /**
     * 搜索,并分页显示
     * @param key 买家的需求搜索的关键词
     * @param start 起始时间
     * @param end 终止时间
     * @param pageSize 每页显示记录数
     * @param pageNumber 当前页号
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "search",method = RequestMethod.POST)
    public ResultBean search(@RequestParam(value = "key") String key,
                             @RequestParam(value = "start") String start, @RequestParam(value = "end") String end,
                             @RequestParam(value = "pageNumber", defaultValue = "1")Integer pageNumber,
                             @RequestParam(value = "pageSize", defaultValue = "10")Integer pageSize,
                             @RequestParam(value = "patentType", defaultValue = "0")Byte patentType,
                             @RequestParam(value = "patentStatus", defaultValue = "0")Byte patentStatus,
                             @RequestParam(value = "selectAll", defaultValue = "false")Boolean selectAll) {
        logger.debug("工作人员搜索案子，寻找卖家，买家的需求信息 demand : "+key);
        logger.debug("批量转换，是否全部转换：" + selectAll);
        if (!selectAll) {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            logger.debug("转换时间选择：" + formatter.format(start) + " - " + formatter.format(end));
        }
        ResultBean rb = new ResultBean();

        // TODO 获取userId，整合进shiro
        Long userId = 1L;// userid目前默认为1
        User user = new User();
        user.setId(userId);


        if (StringUtils.isEmpty(key)) {
            rb.setMessageCode(MessageCode.QUERY_KEYWORD_NULL);
            rb.setMessage("搜索关键词为空");
            return rb;
        }


        // 搜索记录插入数据库
        PatentSupplyQuery entity = new PatentSupplyQuery(user, key, new Date());
        patentSupplyQueryService.save(entity);

//        LocalDate startDate = LocalDate.parse(start);
//        LocalDate endDate = LocalDate.parse(end);

        // 若要做到专利名所含要素的匹配，势必要精准的分词
        // 可能要借助solr等搜索引擎解决方案，到时候怎样分页也是问题
        // 现在先通过OR的方式进行条件查询

        // 分词
        Result result = participleService.parse(key);

        List<Term> terms = result.getTerms(); //拿到terms
        Set<String> participleResultSet = new HashSet<>();// 用于查询的分词结果集
        List<Term> messageTerms = new ArrayList<Term>();

        for(int i=0; i<terms.size(); i++) {
            String word = terms.get(i).getName(); //拿到词
            String natureStr = terms.get(i).getNatureStr(); //拿到词性
            if(participleConfig.getExpectedNature().contains(natureStr)) {
                logger.debug("分词："+word);
                participleResultSet.add(word);
                messageTerms.add(terms.get(i));
            }
        }

        // 对专利类型和专利状态筛选
        // 先排除错误
        switch (patentType) {
            case 0: break; // 所有
            case 1: // 专利
            case 2: // 实用新型
            case 3: // 外观设计
//                patentExample.createCriteria().andTypeEqualTo(patentType);
                break;
            default:
                logger.debug("专利类型不正确");
                rb.setMessage("专利类型不正确");
                rb.setMessageCode(MessageCode.INCORRECT_PATENT_TYPE);
                return rb;
        }

        switch (patentStatus) {
            case 0: break; // 所有
            case 1: // 未缴费
            case 2: // 已下证
//                patentExample.createCriteria().andLawStatusEqualTo(patentStatus);
                break;
            default:
                logger.error("专利状态不正确");
                rb.setMessage("专利状态不正确");
                rb.setMessageCode(MessageCode.INCORRECT_PATENT_STATUS);
                return rb;
        }

        // 循环分词结果集，对每一项进行查询
        PatentExample patentExample = new PatentExample();

        if (!selectAll) {
            patentExample.setStart(start);
            patentExample.setEnd(end);
        }

        patentExample.setOrderByClause("supply_time desc");
        // a and (b or c) = (a and b) or (a and c)
        for (String s : participleResultSet) {
            if (patentType == 0 && patentStatus == 0) {
                patentExample.or().andNameLike("%"+s+"%");
            }else if (patentType == 0 && patentStatus != 0) {
                patentExample.or().andNameLike("%"+s+"%").andLawStatusEqualTo(patentStatus);
            }else if (patentType != 0 && patentStatus == 0) {
                patentExample.or().andNameLike("%"+s+"%").andTypeEqualTo(patentType);
            }else if (patentType != 0 && patentStatus != 0) {
                patentExample.or().andNameLike("%"+s+"%").andTypeEqualTo(patentType).andLawStatusEqualTo(patentStatus);
            }
        }


        // 分页查询
        PageHelper.startPage(pageNumber, pageSize);
        final List<PatentSupply> patentSupplies = patentSupplyService.selectByPatentExample(patentExample);

        Page patentSuppliesPage = (Page) patentSupplies;
        QueryResult<PatentSupply> queryResult = new QueryResult<PatentSupply>();
        queryResult.setCurrentPage(pageNumber);
        queryResult.setShowNum(pageSize);
        queryResult.setPages(patentSuppliesPage.getTotal(), pageSize);
        queryResult.setItems(patentSupplies);

        // 返回结果
        rb.setMessage(messageTerms.toString());
        rb.setData(queryResult);

        return rb;
    }

    @ResponseBody
    @RequestMapping(value = "getList",method = RequestMethod.POST)
    public ResultBean getList(@RequestParam(value = "pageNumber", defaultValue = "1")Integer pageNumber,
                             @RequestParam(value = "pageSize", defaultValue = "10")Integer pageSize){

        ResultBean rb = new ResultBean();
//        PatentExample patentExample = new PatentExample();
//        patentExample.setOrderByClause("supply_time desc");

        // 分页查询
        PageHelper.startPage(pageNumber, pageSize);
        final List<PatentSupply> patentSupplies = patentSupplyService.getAll();

        Page patentSuppliesPage = (Page) patentSupplies;
        QueryResult<PatentSupply> queryResult = new QueryResult<PatentSupply>();
        queryResult.setCurrentPage(pageNumber);
        queryResult.setShowNum(pageSize);
        queryResult.setPages(patentSuppliesPage.getTotal(), pageSize);
        queryResult.setItems(patentSupplies);

        // 返回结果
        rb.setData(queryResult);

        return rb;
    }

    @ResponseBody
    @RequestMapping(value = "addSupply", method = RequestMethod.POST)
    public ResultBean addSupply(Patent patent, PatentSupply patentSupply) {
        ResultBean rb = new ResultBean();

        // 需要对填入的数据进行校验
        if (StringUtils.isEmpty(patent.getId())) {
            rb.setMessageCode(MessageCode.PATENT_ID_NULL);
            rb.setMessage("专利号不能为空");
            return rb;
        }
        if (StringUtils.isEmpty(patent.getName())) {
            rb.setMessageCode(MessageCode.PATENT_NAME_NULL);
            rb.setMessage("专利名称不能为空");
            return rb;
        }
        if (patent.getId().length() > 14) {
            rb.setMessageCode(MessageCode.INCORRECT_PATENT_ID);
            rb.setMessage("专利号不符规范");
            return rb;
        }
        if (patent.getType() == null) {
            rb.setMessageCode(MessageCode.INCORRECT_PATENT_TYPE);
            rb.setMessage("专利类型不正确");
            return rb;
        }
        if (patent.getLawStatus() == null) {
            rb.setMessageCode(MessageCode.INCORRECT_PATENT_STATUS);
            rb.setMessageCode("专利状态不正确");
            return rb;
        }

        // 对Excel中部分解析错误，提高兼容性、鲁棒性
        patent.setId(patent.getId().trim());


//        PatentSupply patentSupply = new PatentSupply();

        // LocalDate 转 Date
//        LocalDate localDate = LocalDate.parse(supplyTime);
//        ZoneId zoneId = ZoneId.systemDefault();
//        ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
//        Date supplyTimeDate = Date.from(zdt.toInstant());
//        patentSupply.setSupplyTime(supplyTimeDate);

        // 配置了转换器
//        patentSupply.setSupplyTime(supplyTime);

        // 根据QQ查找用户
        UserExample userExample = new UserExample();
        userExample.createCriteria().andQqEqualTo(patentSupply.getUser().getQq());
        List<User> users = userService.selectByExample(userExample);
        if (users==null || users.size()==0) { // 数据库中无用户QQ记录
            User user0 = new User();
            user0.setQq(patentSupply.getUser().getQq());
            userService.save(user0);
            patentSupply.setUser(user0);
        }else {
            User user0 = users.get(0);
            patentSupply.getUser().setId(user0.getId());
        }

        // 看专利号是否已存在
        PatentExample patentExample = new PatentExample();
        patentExample.createCriteria().andIdEqualTo(patent.getId());
        List<Patent> patents = patentService.selectByExample(patentExample);
        if (patents == null || patents.size()==0){// 该专利号数据库中不存在
            patentService.save(patent);
            patentSupply.setPatent(patent);
        }else {
            if (! patents.get(0).getName().trim().equals(patent.getName().trim())) { // 专利号相同，而专利名称不同，以数据库中记录为准，已存在
                rb.setMessageCode(MessageCode.PATENT_ID_ALREADY_EXISTS);
                rb.setMessage("该专利号数据库已存在，专利名称不吻合");
                return rb;
            } else { // 专利相同
                patentSupply.setPatent(patents.get(0));
            }
        }


        // 新增需求记录
        final int saved = patentSupplyService.save(patentSupply);

        if (saved<=0) {
            rb.setMessageCode(MessageCode.SAVE_FAILURE);
            rb.setMessage("保存失败");
        }else {
            rb.setMessageCode(MessageCode.SUCCESS);
            rb.setMessage("保存成功");
        }
        return rb;
    }

    @ResponseBody
    @RequestMapping(value = "import", method = RequestMethod.POST)
    public ResultBean batchImport(@RequestParam("excelFile") MultipartFile excelFile) {
        ResultBean rb = new ResultBean();
        Map<String, Object> data = new HashMap<String, Object>();

        Cache cache = springCacheManager.getCache(EhCacheKeys.VERIFICATION_CODE_CACHE);

        long taskId = TASK_ID_NUMBER.getAndIncrement();
        data.put("taskId", taskId);

        try {
            patentSupplyService.asyncTask(new AbstractTask(taskId){
                @Override
                public void execute() {
                    logger.debug("task {} is executing.", taskId);
                    Date today = new Date();
                    //上传
                    InputStream inputs = null;
                    FileOutputStream out = null;
                    ReadExcel readExcel = null;
                    StringBuffer stringBuffer = null;
                    int successRows = 0;
                    int errorRows = 0;
                    try {
                        inputs = excelFile.getInputStream();
                        String fileName = excelFile.getOriginalFilename();
                        File file = new File(fileUploadConfig.getUploadPath()+ today.getTime() + " - " + fileName);
                        FileUtils.copyInputStreamToFile(inputs, file);
                        logger.debug("文件 {} 上传完成", excelFile.getOriginalFilename());

                        // 解析Excel，导入MySQL
                        readExcel=new ReadExcel(5);
                        // 记录错误
                        File errorRecordFile = new File(fileUploadConfig.getUploadPath() + "错误记录-" + today.getTime() + ".txt");
                        out = new FileOutputStream(errorRecordFile);
                        OutputStreamWriter outWriter = new OutputStreamWriter(out, "UTF-8");
                        BufferedWriter bufWrite = new BufferedWriter(outWriter);

                        bufWrite.write("第[i]行，表头为第0行，i从1开始");
                        bufWrite.newLine();

                        // 读取Excel文件
                        readExcel.getExcelWorkbook(file.getAbsolutePath());

                        // 进度
                        Progress progress = new Progress();
                        progress.setTotal(readExcel.getTotalRows()-1);
                        progress.setCompleted(0);
                        progress.setPercentage(0F);

                        PatentExample patentExample = new PatentExample();
                        UserExample userExample = new UserExample();

                        for(int i = 1; i < readExcel.getTotalRows(); i++){
                            List<String> list = readExcel.getLine(i);

                            if (list == null || list.size() < 5) {
                                break;
                            }

                            Patent patent = new Patent();
                            PatentSupply patentSupply = new PatentSupply();

                            patent.setId(list.get(0).trim()); // 专利号,提高兼容性、鲁棒性
                            patent.setName(list.get(1).trim()); // 专利名称
                            patent.setType(Patent.PatentTypeEnum.getIndex(list.get(2))); // 专利类型
                            patent.setLawStatus(Patent.PatentStatusEnum.getIndex(list.get(3))); // 专利状态

                            // 1. 校验专利
                            if (StringUtils.isEmpty(patent.getId())) {
                                bufWrite.write("第" + i + "行 专利号为空");
                                bufWrite.newLine();

                                updateProgressAndCache(progress);
                                continue;
                            }
                            if (patent.getId().length() > 13) {
                                bufWrite.write("第" + i + "行 专利号不符规范");
                                bufWrite.newLine();

                                updateProgressAndCache(progress);
                                continue;
                            }
                            if (StringUtils.isEmpty(patent.getName())) {
                                bufWrite.write("第" + i + "行 专利名称不能为空");
                                bufWrite.newLine();

                                updateProgressAndCache(progress);
                                continue;
                            }
                            // TODO 还有其他校验，抽象出来

                            // 2. 专利号是否存在
                            patentExample.clear();
                            patentExample.createCriteria().andIdEqualTo(patent.getId());
                            List<Patent> patents = patentService.selectByExample(patentExample);
                            if (patents == null || patents.size()==0){// 该专利号数据库中不存在
                                patentService.save(patent);
                                patentSupply.setPatent(patent);
                            }else {
                                // TODO 改为equals比较试试
                                // 专利号相同，而专利名称不同，以数据库中记录为准，已存在
                                if (! patents.get(0).getName().trim().equals(patent.getName().trim())) {
                                    bufWrite.write("第" + i + "行 专利号数据库已存在，专利名称不吻合");
                                    bufWrite.newLine();

                                    updateProgressAndCache(progress);
                                    continue;
                                } else { // 专利相同
                                    patentSupply.setPatent(patents.get(0));
                                    bufWrite.write("第" + i + "行 数据库已有该专利");
                                    bufWrite.newLine();

                                    updateProgressAndCache(progress);
                                    continue;
                                }
                            }

                            // 3. 根据QQ查找用户
                            userExample.clear();
                            userExample.createCriteria().andQqEqualTo(list.get(4)); // QQ 号
                            List<User> users = userService.selectByExample(userExample);
                            if (users==null || users.size()==0) { // 数据库中无用户QQ记录
                                User user = new User();
                                user.setQq(list.get(4));
                                userService.save(user);
                                patentSupply.setUser(user);
                            }else {
                                User user = users.get(0);
                                patentSupply.setUser(user);
                            }

                            patentSupply.setSupplyTime(today);
                            final int saved = patentSupplyService.save(patentSupply);
                            if (saved > 0) {
                                successRows ++ ;
                            } else {
                                errorRows ++;
                            }

                            // 每处理完patent_demand_query中1条记录，在cache中更新记录
                            updateProgressAndCache(progress);
                        }
                        bufWrite.write("------------------");
                        bufWrite.write("成功导入"+successRows+"条");
                        bufWrite.flush();

                        stringBuffer = new StringBuffer("成功导入" + successRows + "条");
                        if (successRows + 1  != readExcel.getTotalRows()) {
                            stringBuffer.append("，详见错误记录");
                        }

                        cache.put(getId() + "-message", stringBuffer.toString());
                        cache.put(getId() + "-errorRecordFileName", errorRecordFile.getName());

                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (readExcel != null) {
                            readExcel.closeStream();
                        }
                        if (out != null) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                private void updateProgressAndCache(Progress progress) {
                    // 每处理完patent_demand_query中1条记录，在cache中更新记录
                    progress.setCompleted(progress.getCompleted() + 1);
                    // 根据taskId，设置进度
                    logger.debug("task {} 目前进度：{}", taskId, progress.getPercentage());
                    cache.put(taskId, progress.getPercentage());
                }
            });
        } catch (TaskException e) {
            e.printStackTrace();
        }

        rb.setData(data);
        return rb;
    }

    @ResponseBody
    @RequestMapping(value = "getProgress", method = RequestMethod.POST)
    public ResultBean getProgress(Long taskId) {
        ResultBean rb = new ResultBean();
        Map<String, Object> data = new HashMap<String, Object>(2);
        String process = patentSupplyService.getProcess(taskId);
        // 将清理缓存的任务交给查看进度
        if (process.startsWith(Progress.COMPLETE_PERCENTAGE)) {
            patentSupplyService.clearCache(taskId);

            Cache cache = springCacheManager.getCache(EhCacheKeys.VERIFICATION_CODE_CACHE);
            data.put("message", cache.get(taskId + "-message").get());
            data.put("fileName", cache.get(taskId + "-errorRecordFileName").get());
        }
        data.put("taskId", taskId);
        data.put("progress", process);
        rb.setData(data);
        return rb;
    }

    @RequestMapping("download")
    public void downFile(@RequestParam("fileName") String fileName, HttpServletResponse response) {
        // 得到要下载的文件名
        try {
//            fileName = new String(fileName.getBytes("UTF-8"), "UTF-8");
            // 得到要下载的文件
            File file = new File(fileUploadConfig.getUploadPath() + fileName);

            // 如果文件不存在
            if (!file.exists()) {
                // 文件已被删除！！
                return;
            }
            // 处理文件名
//            String realname = fileName.substring(0, fileName.lastIndexOf("."));
            // 设置响应头，控制浏览器下载该文件
            response.setHeader("content-disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, "UTF-8"));
            // 读取要下载的文件，保存到文件输入流
            FileInputStream in = new FileInputStream(file);
            // 创建输出流
            OutputStream out = response.getOutputStream();
            // 创建缓冲区
            byte buffer[] = new byte[1024];
            int len = 0;
            // 循环将输入流中的内容读取到缓冲区当中
            while ((len = in.read(buffer)) > 0) {
                // 输出缓冲区的内容到浏览器，实现文件下载
                out.write(buffer, 0, len);
            }
            // 关闭文件输入流
            in.close();
            // 关闭输出流
            out.close();
        } catch (Exception e) {

        }
    }

    @ResponseBody
    @RequestMapping(value = "deleteIds", method = RequestMethod.POST)
    public ResultBean deleteIds(@RequestParam(value = "ids[]") Long[] ids) {
        ResultBean rb = new ResultBean();

        boolean allSuccess = true;

        for (Long id : ids) {
            PatentSupply patentSupply = patentSupplyService.selectByKey(id);
            final int deleted = patentService.delete(Long.valueOf(patentSupply.getPatent().getId()));
            allSuccess &= deleted > 0;

            final int deleted2 = patentSupplyService.delete(id);
            allSuccess &= deleted2 > 0;
        }



        if (allSuccess) {
            rb.setMessageCode(MessageCode.SUCCESS);
            rb.setMessage("都删除成功");
        }else {
            rb.setMessageCode(MessageCode.DELETE_FAILURE);
            rb.setMessage("删除失败，或部分删除成功");
        }

        return rb;
    }
}
